package com.trg.flex.frm
{
	import com.trg.flex.FlexUtils;
	import com.trg.flex.ListWithPrepend;
	import com.trg.flex.frm.validation.InputValidationHelper;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.FocusEvent;
	
	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	import mx.controls.ComboBox;
	import mx.events.CollectionEvent;
	import mx.events.FlexEvent;
	import mx.events.ListEvent;

	[Event(name="validChanged", type="mx.events.PropertyChangeEvent")]
	public class FormComboBox extends ComboBox implements FormInput
	{
		//FormInput
		protected var formContainer:FormContainer;
		protected var topContainer:DisplayObject;
		protected var _value:Object
		
		//ComboBox specific
		protected static const NULL_OPTION : Object = new Object();
		protected var _property:String;
		protected var _dataProvider:IList;
		protected var _blankValue:* = null;
		protected var _allowBlank:Boolean = false;
		protected var _view:ListWithPrepend = new ListWithPrepend();
		protected var _labelFunction:Function;
		protected var _labelField:String;
		
		
		public function FormComboBox() {
			super();
			topContainer = this;
			topContainer.addEventListener(FlexEvent.ADD, addToFormHandler);
			prompt = "Select One"
		}
		
	//FormInput INTERFACE
		public function get property():String {
			return _property;
		}
		
		public function set property(property:String):void {
			_property = property;
		}
		
		public function set elementValue(value:Object):void {
			_value = value;
			setComboBoxSelection();
			validationHelper.setNotValidated();
		}
		
		public function get elementValue():Object {
			if (selectedIndex != -1) {
				if (selectedItem === NULL_OPTION) {
					_value = null;
				} else if (valueField != null && valueField.length > 0) {
					_value = FlexUtils.getNestedProperty(selectedItem, valueField);
				} else {
					_value = selectedItem;
				}
			}
			return _value;
		}
		
		
	//Validation
		protected var validationHelper:InputValidationHelper = new InputValidationHelper(this as FormInput, "elementValue", ListEvent.CHANGE, FocusEvent.FOCUS_OUT);
		
		[Bindable("validChanged")]
		public function get valid():Boolean {
			return validationHelper.valid;
		}
		
		public function isInvalidRequired():Boolean {
			return validationHelper.isInvalidRequired();
		}
		
		public function clearValidation():void {
			errorString = "";
		}
		
		public function set type(value:Class):void {
			validationHelper.type = value;
		}
		
		public function setMetaData(metaData:Object):void {
			validationHelper.setMetaData(metaData);
		}
		
		public function set notEmpty(value:Boolean):void {
			validationHelper.setMetaDataProp("notEmpty", value ? true : undefined);
		}
		
		public function set notNull(value:Boolean):void {
			validationHelper.setMetaDataProp("notNull", value ? true : undefined);
		}
		
		public function set maxValue(value:Object):void {
			validationHelper.setMetaDataProp("maxValue", value != null ? value : undefined);
		}
		
		public function set minValue(value:Object):void {
			validationHelper.setMetaDataProp("minValue", value != null ? value : undefined);
		}
		
	
	//FormInput helpers	
		protected function addToFormHandler(event:FlexEvent):void {
			topContainer.removeEventListener(FlexEvent.ADD, addToFormHandler);
			
			while(true) {
				if(topContainer.parent is FormContainer) {
					formContainer = topContainer.parent as FormContainer;
					formContainer.addElement(this);
					trace("addElement");
					break;
				} else if (topContainer.parent == null) {
					topContainer.addEventListener(FlexEvent.ADD, addToFormHandler);
					break;
				}
				topContainer = topContainer.parent;
			}
		}
		
		
	//COMBO BOX CODE
		[Bindable]
		public var compareField:String;
		
		[Bindable]
		public var valueField:String;
		
		[Bindable]
		public var blankText:String = "[ None ]";
		
		[Bindable]
		public function get blankValue():String {
			return _blankValue;
		}
		public function set blankValue(value:String):void {
			_blankValue = value;
			setComboBoxSelection();
		}
		
		[Bindable]
		public function get allowBlank():Boolean {
			return _allowBlank;
		}
		public function set allowBlank(value:Boolean):void {
			if (value == _allowBlank) return;
			_allowBlank = value;
			if (!value) {
				super.dataProvider = _dataProvider;
				super.labelFunction = _labelFunction;
				setComboBoxSelection();
			} else {
				_view.list = _dataProvider;
				_view.prepend = NULL_OPTION;
				super.dataProvider = _view;
				super.labelFunction = myLabelFunction;
				setComboBoxSelection();
			}
		}
		
		[Bindable]
		public function get options():*{
			return _dataProvider;	
		}
		public function set options(value:*):void {
			//if a nullOption is set, use the _view, which has the nullOption
			// prepended to the list
			trace("set options");
			if(_dataProvider != null)
				_dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, dataProviderChangeHandler);
			
			if (value is IList) {
				_dataProvider = value as IList;
			} else if (value is Array) {
				_dataProvider = new ArrayCollection(value as Array);
			} else {
				_dataProvider = null;
			}
			if(_allowBlank) {
				_view.list = _dataProvider;
			} else {
				super.dataProvider = _dataProvider;
			}
			
			if(_dataProvider != null)
				_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, dataProviderChangeHandler);
			
			setComboBoxSelection();
		}
		
		override public function set labelFunction(value:Function):void {
			_labelFunction = value;
			if (!_allowBlank) super.labelFunction = _labelFunction;
		}
		
		override public function set labelField(value:String):void {
			_labelField = value;
			super.labelField = value;
		}
		
		
	//HELPER FUNCTIONS
		protected function dataProviderChangeHandler(event:Event):void {
			setComboBoxSelection();
		}
		
		protected function myLabelFunction(o:*):String {
			if (o === NULL_OPTION && _allowBlank) {
				return blankText;
			} else if (o == null) {
				return "";
			} else if (_labelFunction != null) {
				return _labelFunction(o);
			} else if (_labelField != null) {
				return FlexUtils.getNestedProperty(o,labelField) as String;
			} else {
				return o.toString();
			}
		}
	
		protected function setComboBoxSelection():void {
			if (_value == null || (_allowBlank && _value == _blankValue)) {
				//for null value use nullOption if set, otherwise select none (-1)
				selectedIndex = (_allowBlank) ? 0 : -1;
				return;
			} else if (_dataProvider == null || _dataProvider.length == 0) {
				//if there are no options in the dataProvider (and the value
				//is not null so it cannot be the nullOption), select none (-1)
				selectedIndex = -1;
				return;
			}
			
			var i:int;
			if (valueField != null) {
				if (FlexUtils.getNestedProperty(super.selectedItem, valueField) == _value) {
					//if it's already correct, don't bother.
					return;
				} else {
					i = FlexUtils.indexOfFieldValue(_dataProvider, _value, valueField);
				}
			} else {
				if (FlexUtils.getNestedProperty(selectedItem, compareField) == FlexUtils.getNestedProperty(_value, compareField)) {
					//if it's already correct, don't bother.
					return;
				} else {
					i = FlexUtils.indexOf(_dataProvider, _value, compareField);
				}
			}
			
			selectedIndex = (_allowBlank) ? i + 1 : i;
		}
	}
}